#ifndef _BLOKINGQUEUE_H
#define _BLOKINGQUEUE_H

#include <iostream>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>

template <typename T>
class BlockingQueue
{
public:
    BlockingQueue(bool noblock = false);
    ~BlockingQueue();
    void Push(T &value);        //入队操作
    bool Pop(T &value);         //出队操作
    void Cancel();              //撤销阻塞

private:
    bool m_noblock;
    std::mutex m_mutex;
    std::queue<T> m_queue;
    std::condition_variable m_condivr;
};

/********************************类成员函数实现******************************/
template <typename T>
BlockingQueue<T>::BlockingQueue(bool noblock):m_noblock(noblock)
{

}

template <typename T>
BlockingQueue<T>::~BlockingQueue()
{

}

template <typename T>
void BlockingQueue<T>::Push(T &value)
{
    std::lock_guard<std::mutex> lock(m_mutex);
    m_queue.push(value);
    m_condivr.notify_one();
}

template <typename T>
bool BlockingQueue<T>::Pop(T &value)
{
    std::unique_lock<std::mutex> lock(m_mutex);
    m_condivr.wait(lock,[this](){
        return (!m_queue.empty() || m_noblock);
    });

    if (m_queue.empty())//手动唤醒
    {
        return false;
    }

    value = m_queue.front();
    m_queue.pop();
    return true;
}

template <typename T>
void BlockingQueue<T>::Cancel()
{
    m_noblock = true;
    m_condivr.notify_all();
}

/********************************优化******************************/
template <typename T>
class BlockingQueuePro
{
public:
    BlockingQueuePro(bool noblock = false):m_noblock(noblock){}
    ~BlockingQueuePro(){};
    void Push(T &value);
    bool Pop(T &value);
    void Cancel();

private:
    bool swap_queue(){
        std::unique_lock<std::mutex> lock(m_prod_mutex);
        m_condva.wait(lock,[this]{return !m_prod_queue.empty() || m_noblock;});
        std::swap(m_cons_queue,m_prod_queue);
        return m_cons_queue.size();
    }

    bool m_noblock;
    std::queue<T> m_prod_queue;
    std::queue<T> m_cons_queue;
    std::mutex m_prod_mutex;
    std::mutex m_cond_mutex;
    std::condition_variable m_condva;
};

template <typename T>
inline void BlockingQueuePro<T>::Push(T &value)
{
    std::lock_guard<std::mutex> lock(m_prod_mutex);
    m_prod_queue.push(value);
    m_condva.notify_one();
}

template <typename T>
inline bool BlockingQueuePro<T>::Pop(T &value)
{
    std::unique_lock<std::mutex> lock(m_cond_mutex);
    if (m_cons_queue.empty() && swap_queue() == 0)
    {
        return false;
    }
    value = m_cons_queue.front();
    m_cons_queue.pop();
    return true;
}

template <typename T>
inline void BlockingQueuePro<T>::Cancel()
{
    std::lock_guard<std::mutex> lock(m_prod_mutex);
    m_noblock = true;
    m_condva.notify_all();
}

#endif